package com.huaboot.system.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.huaboot.commont.exception.BusinessException;
import com.huaboot.commont.exception.ErrorCode;
import com.huaboot.framework.redis.config.RedisCache;
import com.huaboot.framework.security.user.JwtTokenUserInfo;
import com.huaboot.framework.security.user.LoginUser;
import com.huaboot.framework.security.utils.JwtUtil;
import com.huaboot.system.convert.SysUserConvert;
import com.huaboot.system.entity.SysUserEntity;
import com.huaboot.system.mapper.SysUserMapper;
import com.huaboot.system.service.SysUserDetailsService;
import com.huaboot.system.service.SysUserService;
import com.huaboot.system.vo.AuthLoginRespVO;
import lombok.AllArgsConstructor;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.huaboot.commont.page.PageResult;
import com.huaboot.system.convert.SysUserTokenConvert;
import com.huaboot.system.entity.SysUserTokenEntity;
import com.huaboot.system.query.SysUserTokenQuery;
import com.huaboot.system.vo.SysUserTokenVO;
import com.huaboot.system.mapper.SysUserTokenMapper;
import com.huaboot.system.service.SysUserTokenService;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

import cn.hutool.core.util.ObjectUtil;




/**
 * 用户Token
 *
 * @author ff xxx
 * @since 1.0.0 2023-09-04
 */
@Service
@AllArgsConstructor
public class SysUserTokenServiceImpl extends ServiceImpl<SysUserTokenMapper, SysUserTokenEntity> implements SysUserTokenService {

    private final RedisCache redisCache;

    private final SysUserMapper sysUserService;

    private final SysUserDetailsService sysUserDetailsService;

    @Override
    public PageResult<SysUserTokenVO> page(SysUserTokenQuery query) {
        PageHelper.startPage(query.currentPage, query.size);
        List<SysUserTokenEntity> SysUserTokenEntityList = list(getWrapper(query));
        PageInfo<SysUserTokenEntity> page = new PageInfo<>(SysUserTokenEntityList);
        return new PageResult<>(SysUserTokenConvert.INSTANCE.convertList(page.getList()), page.getTotal());
    }

    private LambdaQueryWrapper<SysUserTokenEntity> getWrapper(SysUserTokenQuery query){
        LambdaQueryWrapper<SysUserTokenEntity> wrapper = Wrappers.lambdaQuery();
        return wrapper;
    }

    @Override
    public void save(SysUserTokenVO vo) {
        SysUserTokenEntity entity = SysUserTokenConvert.INSTANCE.convert(vo);
         save(entity);
    }

    @Override
    public void update(SysUserTokenVO vo) {
        SysUserTokenEntity entity = SysUserTokenConvert.INSTANCE.convert(vo);

        updateById(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(List<Long> idList) {
        removeByIds(idList);
    }

    @Override
    public void updateExpireToken(Long userId) {
        SysUserTokenEntity entity = new SysUserTokenEntity();
        entity.setAccessTokenExpire(new Date());
        entity.setRefreshTokenExpire(new Date());
        baseMapper.update(entity, new LambdaQueryWrapper<SysUserTokenEntity>().eq(SysUserTokenEntity::getUserId, userId));
    }

    @Override
    public AuthLoginRespVO createTokenUserInfo(LoginUser loginUser) {
        AuthLoginRespVO authLoginRespVO = new AuthLoginRespVO();
        //缓存redis
        JwtTokenUserInfo jwtTokenUserInfo = JwtUtil.getJwtTokenUserInfo(String.valueOf(loginUser.getUserId()));
        //authenticate存入redis
        redisCache.setCacheObject("login:"+loginUser.getUserId(),loginUser,JwtUtil.JWT_TTL);
        // 是否存在Token
        SysUserTokenEntity tokenEntity = baseMapper.selectOne(new LambdaQueryWrapper<SysUserTokenEntity>().eq(SysUserTokenEntity::getUserId, loginUser.getUserId()));
        SysUserTokenEntity sysUserTokenEntity = new SysUserTokenEntity();
        sysUserTokenEntity.setUserId(loginUser.getUserId());
        BeanUtil.copyProperties(jwtTokenUserInfo,sysUserTokenEntity);
        if (tokenEntity == null) {
            baseMapper.insert(sysUserTokenEntity);
        } else {
            sysUserTokenEntity.setId(tokenEntity.getId());
            baseMapper.updateById(sysUserTokenEntity);
        }
        //设置令牌
        BeanUtil.copyProperties(jwtTokenUserInfo,authLoginRespVO);
        return authLoginRespVO;
    }

    @Override
    public AuthLoginRespVO refreshToken(String refreshToken) {
        //查询刷新token的记录，并且没有过期
        LambdaQueryWrapper<SysUserTokenEntity> query = Wrappers.lambdaQuery();
        query.eq(SysUserTokenEntity::getRefreshToken, refreshToken);
        query.ge(SysUserTokenEntity::getRefreshTokenExpire, new Date());
        SysUserTokenEntity entity = baseMapper.selectOne(query);
        if (entity == null) {
            throw new BusinessException(ErrorCode.REFRESH_TOKEN_INVALID);
        }

        // 删除缓存信息
        redisCache.deleteObject("login:"+entity.getUserId());

        // 生成新 accessToken
        JwtTokenUserInfo jwtTokenUserInfo = JwtUtil.getJwtTokenUserInfo(String.valueOf(entity.getUserId()));
        entity.setAccessToken(jwtTokenUserInfo.getAccessToken());
        entity.setAccessTokenExpire(jwtTokenUserInfo.getAccessTokenExpire());
        // 更新
        baseMapper.updateById(entity);

        // 设置用户权限信息
        SysUserEntity user = sysUserService.getById(entity.getUserId());
        // 保存用户信息到缓存
        LoginUser loginUser = SysUserConvert.INSTANCE.convertDetail(user);
        loginUser.setUserId(entity.getUserId());
        sysUserDetailsService.getUserDetails(loginUser);
        //用户信息保存缓存
        redisCache.setCacheObject("login:"+entity.getUserId(),loginUser,JwtUtil.JWT_TTL);
        AuthLoginRespVO authLoginRespVO = new AuthLoginRespVO();
        authLoginRespVO.setAccessToken(entity.getAccessToken());
        authLoginRespVO.setAccessTokenExpire(entity.getAccessTokenExpire());
        return authLoginRespVO;
    }

    @Override
    @Async
    public void updateCacheAuthByRoleId(Long roleId) {
        // 查询角色下的所有用户
        List<Long> userIdList = baseMapper.getOnlineAccessTokenListByRoleId(roleId, new Date());
        userIdList.forEach(this::updateCacheAuth);
    }

    @Override
    @Async
    public void updateCacheAuthByUserId(Long userId) {
        // 根据用户ID更新用户角色
        updateCacheAuth(userId);
    }

    /**
     * 根据accessToken，更新Cache里面的用户权限
     */
    private void updateCacheAuth(Long userId) {
        //用userid--key去redis过去用户信息
        String redisKey = "login:" + userId;
        LoginUser loginUser = redisCache.getCacheObject(redisKey);
        // 用户不存在
        if (loginUser == null) {
            return;
        }

        // 查询过期时间
        Long expire = redisCache.getExpire(redisKey);
        if (expire == null) {
            return;
        }
        // 设置用户权限信息
        sysUserDetailsService.getUserDetails(loginUser);
        // 更新缓存
        redisCache.setCacheObject(redisKey,loginUser,expire);

    }
}
